// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef REMOTING_HOST_FILE_TRANSFER_IPC_FILE_OPERATIONS_H_
#define REMOTING_HOST_FILE_TRANSFER_IPC_FILE_OPERATIONS_H_

#include <cstdint>
#include <tuple>
#include <vector>

#include "base/containers/flat_map.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "remoting/host/file_transfer/file_operations.h"
#include "remoting/protocol/file_transfer_helpers.h"

namespace remoting {

// Implementation of FileOperations that translates the interface into easy-to-
// serialize requests that can be forwarded over an IPC channel.
// IpcFileOperationsRequestHandlerImpl can be used on the remote end to
// perform the requested operations.
class IpcFileOperations : public FileOperations {
 public:
  // Handles requests generated by IpcFileOperations instances by either
  // performing the requested operations directly or forwarding them to another
  // process.
  class RequestHandler {
   public:
    virtual ~RequestHandler() = default;

    virtual void ReadFile(std::uint64_t file_id) = 0;
    virtual void ReadChunk(std::uint64_t file_id, std::uint64_t size) = 0;
    virtual void WriteFile(std::uint64_t file_id,
                           const base::FilePath& filename) = 0;
    virtual void WriteChunk(std::uint64_t file_id,
                            std::vector<std::uint8_t> data) = 0;
    virtual void Close(std::uint64_t file_id) = 0;
    virtual void Cancel(std::uint64_t file_id) = 0;
  };

  // Handles responses to file operations requests.
  class ResultHandler {
   public:
    using Result = protocol::FileTransferResult<Monostate>;
    using InfoResult =
        protocol::FileTransferResult<std::tuple<base::FilePath, uint64_t>>;
    using DataResult =
        remoting::protocol::FileTransferResult<std::vector<std::uint8_t>>;

    virtual ~ResultHandler() = default;
    virtual void OnResult(std::uint64_t file_id, Result result) = 0;
    virtual void OnInfoResult(std::uint64_t file_id, InfoResult result) = 0;
    virtual void OnDataResult(std::uint64_t file_id, DataResult result) = 0;
  };

  IpcFileOperations(const IpcFileOperations&) = delete;
  IpcFileOperations& operator=(const IpcFileOperations&) = delete;

  ~IpcFileOperations() override;

  // FileOperations implementation.
  std::unique_ptr<Reader> CreateReader() override;
  std::unique_ptr<Writer> CreateWriter() override;

 private:
  using ResultCallback = base::OnceCallback<void(ResultHandler::Result)>;
  using InfoResultCallback =
      base::OnceCallback<void(ResultHandler::InfoResult)>;
  using DataResultCallback =
      base::OnceCallback<void(ResultHandler::DataResult)>;

  class IpcReader;
  class IpcWriter;

  struct SharedState {
   public:
    explicit SharedState(RequestHandler* request_handler);

    SharedState(const SharedState&) = delete;
    SharedState& operator=(const SharedState&) = delete;

    ~SharedState();

    // Send a Cancel request for |file_id| and provide an error response to any
    // pending response callbacks for it. Called in the event of an unexpected
    // message from the Desktop process.
    void Abort(std::uint64_t file_id);

    // File ID to use for the next file opened.
    std::uint64_t next_file_id = 0;

    // Pending callbacks awaiting responses from the desktop process, keyed by
    // the file_id of the waiting Reader or Writer.
    base::flat_map<std::uint64_t, ResultCallback> result_callbacks;
    base::flat_map<std::uint64_t, InfoResultCallback> info_result_callbacks;
    base::flat_map<std::uint64_t, DataResultCallback> data_result_callbacks;

    // The associated RequestHandler.
    raw_ptr<RequestHandler> request_handler;

    base::WeakPtrFactory<SharedState> weak_ptr_factory{this};
  };

  explicit IpcFileOperations(base::WeakPtr<SharedState> shared_state);

  std::uint64_t GetNextFileId();

  // Contains shared state used by all instances tied to a given
  // RequestHandler.
  base::WeakPtr<SharedState> shared_state_;

  friend class IpcFileOperationsFactory;
};

// Creates IpcFileOperations instances for a given RequestHandler. All
// IpcFileOperations instances for the RequestHandler must be created through
// the same IpcFileOperationsFactory.
class IpcFileOperationsFactory : public IpcFileOperations::ResultHandler {
 public:
  // |request_handler| must be valid for the entire lifetime of
  // IpcFileOperationsFactory, and must only be used to construct a single
  // IpcFileOperationsFactory to avoid file ID conflicts.
  IpcFileOperationsFactory(IpcFileOperations::RequestHandler* request_handler);

  IpcFileOperationsFactory(const IpcFileOperationsFactory&) = delete;
  IpcFileOperationsFactory& operator=(const IpcFileOperationsFactory&) = delete;

  ~IpcFileOperationsFactory() override;

  std::unique_ptr<FileOperations> CreateFileOperations();

  // ResultHandler implementation.
  void OnResult(std::uint64_t file_id, Result result) override;
  void OnInfoResult(std::uint64_t file_id, InfoResult result) override;
  void OnDataResult(std::uint64_t file_id, DataResult result) override;

 private:
  IpcFileOperations::SharedState shared_state_;
};

}  // namespace remoting

#endif  // REMOTING_HOST_FILE_TRANSFER_IPC_FILE_OPERATIONS_H_
